package dshumsky.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Iterator;

public class WebServer implements Runnable {

	public static Charset charset = Charset.forName("UTF-8");
	public static CharsetEncoder encoder = charset.newEncoder();
	private Selector selector = Selector.open();
	private ServerSocketChannel server = ServerSocketChannel.open();
	private boolean isRunning = true;
	private boolean debug = true;

	protected WebServer(InetSocketAddress address) throws IOException {
		server.socket().bind(address);
		server.configureBlocking(false);
		server.register(selector, SelectionKey.OP_ACCEPT);
	}

	/**
	 * Core run method. This is not a thread safe method, however it is non
	 * blocking. If an exception is encountered it will be thrown wrapped in a
	 * RuntimeException, and the server will automatically be {@link #shutDown}
	 */
	@Override
	public final void run() {
		if (isRunning) {
			try {
				selector.selectNow();
				Iterator<SelectionKey> i = selector.selectedKeys().iterator();
				while (i.hasNext()) {
					SelectionKey key = i.next();
					i.remove();
					if (!key.isValid()) {
						continue;
					}
					try {
						// get a new connection
						if (key.isAcceptable()) {
							SocketChannel client = server.accept();
							client.configureBlocking(false);
							client.register(selector, SelectionKey.OP_READ);
						} else if (key.isReadable()) {
							SocketChannel client = (SocketChannel) key.channel();
							HTTPSession session = (HTTPSession) key.attachment();
							// create it if it doesnt exist
							if (session == null) {
								session = new HTTPSession(client);
								key.attach(session);
							}
							session.readData();
							String line;
							while ((line = session.readLine()) != null) {
								// check if we have got everything
								if (line.isEmpty()) {
									HTTPRequest request = new HTTPRequest(session.getReadLines().toString());
									session.sendResponse(handle(session, request));
									session.close();
								}
							}
						}
					} catch (Exception ex) {
						System.err.println("Error handling client: " + key.channel());
						if (debug) {
							ex.printStackTrace();
						} else {
							System.err.println(ex);
							System.err.println("\tat " + ex.getStackTrace()[0]);
						}
						if (key.attachment() instanceof HTTPSession) {
							((HTTPSession) key.attachment()).close();
						}
					}
				}
			} catch (IOException ex) {
				// call it quits
				shutdown();
				// throw it as a runtime exception so that Bukkit can handle it
				throw new RuntimeException(ex);
			}
		}
	}

	protected HTTPResponse handle(HTTPSession session, HTTPRequest request) throws IOException {
		HTTPResponse response = new HTTPResponse();
		response.setContent("I liek cates".getBytes());
		return response;
	}

	public final void shutdown() {
		isRunning = false;
		try {
			selector.close();
			server.close();
		} catch (IOException ex) {
			// do nothing, its game over
		}
	}

	public static void main(String[] args) throws Exception {
		WebServer server = new WebServer(new InetSocketAddress(5555));
		while (true) {
			server.run();
			Thread.sleep(100);
		}
	}
}
